/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.project.imported;

import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.project.ImportConflictDTO;
import cn.ac.nercis.pes.service.project.SystemImportService;
import cn.ac.nercis.pes.service.utils.ZipUtils;
import cn.ac.nercis.pes.service.utils.FileManage;
import cn.hutool.crypto.digest.MD5;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.Arrays;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 系统文件导入服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class SystemFileImportService {
    private static final String FILE_SPLIT = "/";
    private static final String BUSINESS_CONTENT_DIR = "business_content";
    private static final String CHECK_POINT_DIR = "check_point";
    private static final String NETWORK_TOPOLOGY_DIR = "network_topology";
    private final static String PHOTO_FILE_DIR = "photo_files";
    private final static String PROOF_FILE_DIR = "proof";
    private static final MD5 md5Provider = MD5.create();

    public Result<List<ImportConflictDTO>> checkSystemFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<ImportConflictDTO> conflictData = checkPointFiles(systemId, importSystemFiles);
        conflictData.addAll(checkNetworkTopology(systemId, importSystemFiles));
        conflictData.addAll(checkBusinessFiles(systemId,importSystemFiles));
        conflictData.addAll(checkPhotoFiles(systemId, importSystemFiles));
        conflictData.addAll(checkProofFiles(systemId, importSystemFiles));
        return Result.success(conflictData);
    }

    public Result<String> importSystemFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        Result<String> result = importCheckPointFiles(systemId, importSystemFiles, cover);
        if (!result.isSuccess()) {
            return result;
        }
        result = importNetworkTopologyFiles(systemId, importSystemFiles, cover);
        if (!result.isSuccess()) {
            return result;
        }
        result = importBusinessContentFiles(systemId, importSystemFiles, cover);
        if (!result.isSuccess()) {
            return result;
        }
        result =  importPhotoFiles(systemId, importSystemFiles, cover);
        if (!result.isSuccess()) {
            return result;
        }
        return importProofFiles(systemId, importSystemFiles, cover);
    }

    private Result<String> importCheckPointFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), CHECK_POINT_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(importFiles)) {
            return Result.success();
        }
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getCheckPointDir(systemId)));
        try {
            for (SystemImportService.ImportEntityFile importFile : importFiles) {
                importCheckPointFile(systemId, importFile, sourceFiles, cover);
            }
            return Result.success();
        } catch (IOException ex) {
            log.error("导入测评工具检查点文件失败：", ex);
            return Result.failed("导入测评工具检查点文件失败");
        }
    }

    private void importCheckPointFile(@NonNull String systemId, SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, Boolean cover) throws IOException {
        File sourceFile = sourceFiles.stream()
                .filter(f -> StringUtils.equals(ZipUtils.getEntityFileName(importFile.getName()), f.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile)) {
            sourceFile = FileUtils.getFile(FileManage.getCheckPointDir(systemId), ZipUtils.getEntityFileName(importFile.getName()));
        } else {
            String sourceMd5 = md5Provider.digestHex16(FileUtils.readFileToByteArray(sourceFile));
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (StringUtils.equals(sourceMd5, importMd5) || !(cover)) {
                return;
            }
        }
        if(FileManage.isImageFile(sourceFile.getPath()) && !Arrays.isNullOrEmpty(importFile.getData())){
            FileManage.copyFile(sourceFile, importFile.getData());
        }
    }

    private Result<String> importNetworkTopologyFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), NETWORK_TOPOLOGY_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(importFiles)) {
            return Result.success();
        }
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getNetworkTopologyDir(systemId)));
        try {
            for (SystemImportService.ImportEntityFile importFile : importFiles) {
                importNetworkTopologyFile(systemId, importFile, sourceFiles, cover);
            }
            return Result.success();
        } catch (IOException ex) {
            log.error("导入网络拓扑图文件失败：", ex);
            return Result.failed("导入网络拓扑图文件失败");
        }
    }

    private void importNetworkTopologyFile(@NonNull String systemId, SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, Boolean cover) throws IOException {
        File sourceFile = sourceFiles.stream()
                .filter(f -> StringUtils.equals(ZipUtils.getEntityFileName(importFile.getName()), f.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile)) {
            sourceFile = FileUtils.getFile(FileManage.getNetworkTopologyDir(systemId), ZipUtils.getEntityFileName(importFile.getName()));
        } else {
            String sourceMd5 = md5Provider.digestHex16(FileUtils.readFileToByteArray(sourceFile));
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (StringUtils.equals(sourceMd5, importMd5) || !(cover)) {
                return;
            }
        }
        if(FileManage.isImageFile(sourceFile.getPath()) && !Arrays.isNullOrEmpty(importFile.getData())){
            FileManage.copyFile(sourceFile, importFile.getData());
        }
    }

    private Result<String> importBusinessContentFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), BUSINESS_CONTENT_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(importFiles)) {
            return Result.success();
        }
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getBusinessContentDir(systemId)));
        try {
            for (SystemImportService.ImportEntityFile importFile : importFiles) {
                importBusinessContentFile(systemId, importFile, sourceFiles, cover);
            }
            return Result.success();
        } catch (IOException ex) {
            log.error("导入承载的业务情况图文件失败：", ex);
            return Result.failed("导入承载的业务情况文件失败");
        }
    }

    private void importBusinessContentFile(@NonNull String systemId, SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, Boolean cover) throws IOException {
        File sourceFile = sourceFiles.stream()
                .filter(f -> StringUtils.equals(ZipUtils.getEntityFileName(importFile.getName()), f.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile)) {
            sourceFile = FileUtils.getFile(FileManage.getBusinessContentDir(systemId), ZipUtils.getEntityFileName(importFile.getName()));
        } else {
            String sourceMd5 = md5Provider.digestHex16(FileUtils.readFileToByteArray(sourceFile));
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (StringUtils.equals(sourceMd5, importMd5) || !(cover)) {
                return;
            }
        }
        if(FileManage.isImageFile(sourceFile.getPath()) && !Arrays.isNullOrEmpty(importFile.getData())){
            FileManage.copyFile(sourceFile, importFile.getData());
        }
    }

    private Result<String> importProofFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        var importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), PROOF_FILE_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(importFiles)) {
            return Result.success();
        }
        List<File> sourceFiles = FileManage.findProofFiles(systemId);
        try {
            for (SystemImportService.ImportEntityFile importFile : importFiles) {
                importProofFile(systemId, importFile, sourceFiles, cover);
            }
            return Result.success();
        } catch (IOException ex) {
            log.error("导入密评证明记录文件失败：", ex);
            return Result.failed("导入密评证明记录文件失败");
        }
    }

    private void importProofFile(@NonNull String systemId, SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, Boolean cover) throws IOException {
        File sourceFile = sourceFiles.stream()
                .filter(f -> StringUtils.equals(ZipUtils.getEntityFileName(importFile.getName()), f.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile)) {
            String fileDir = Paths.get(FileManage.getProofBaseDir(systemId), ZipUtils.getLayerNameByEntityName(importFile.getName())).toString();
            sourceFile = FileUtils.getFile(fileDir, ZipUtils.getEntityFileName(importFile.getName()));
        } else {
            String sourceMd5 = md5Provider.digestHex16(FileUtils.readFileToByteArray(sourceFile));
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (StringUtils.equals(sourceMd5, importMd5) || !(cover)) {
                return;
            }
        }
        if(FileManage.isImageFile(sourceFile.getPath()) && !Arrays.isNullOrEmpty(importFile.getData())){
            FileManage.copyFile(sourceFile, importFile.getData());
        }
    }

    private Result<String> importPhotoFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles, Boolean cover) {
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), PHOTO_FILE_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(importFiles)) {
            return Result.success();
        }
        List<File> sourceFiles = FileManage.findPhotoFiles(systemId);
        try {
            for (SystemImportService.ImportEntityFile importFile : importFiles) {
                importPhotoFile(systemId, importFile, sourceFiles, cover);
            }
            return Result.success();
        } catch (IOException ex) {
            log.error("导入证据文件失败：", ex);
            return Result.failed("导入证据文件失败");
        }
    }

    private void importPhotoFile(@NonNull String systemId, SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, Boolean cover) throws IOException {
        File sourceFile = sourceFiles.stream()
                .filter(f -> StringUtils.equals(ZipUtils.getEntityFileName(importFile.getName()), f.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile)) {
            String fileDir = FileManage.getPhotoFileDir(systemId, ZipUtils.getLayerNameByEntityName(importFile.getName()));
            sourceFile = FileUtils.getFile(fileDir, ZipUtils.getEntityFileName(importFile.getName()));
        } else {
            String sourceMd5 = md5Provider.digestHex16(FileUtils.readFileToByteArray(sourceFile));
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (StringUtils.equals(sourceMd5, importMd5) || !(cover)) {
                return;
            }
        }
        if(FileManage.isImageFile(sourceFile.getPath()) && !Arrays.isNullOrEmpty(importFile.getData())){
            FileManage.copyFile(sourceFile, importFile.getData());
        }
    }

    private List<ImportConflictDTO> checkBusinessFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getBusinessContentDir(systemId)));
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), BUSINESS_CONTENT_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(sourceFiles) || CollectionUtils.isEmpty(importFiles)) {
            return Collections.emptyList();
        }
        return importFiles.stream()
                .map(ef -> checkFile(ef, sourceFiles, "承载的业务情况图"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }


    private List<ImportConflictDTO> checkPointFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getCheckPointDir(systemId)));
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), CHECK_POINT_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(sourceFiles) || CollectionUtils.isEmpty(importFiles)) {
            return Collections.emptyList();
        }
        return importFiles.stream()
                .map(ef -> checkFile(ef, sourceFiles, "检查点拓扑图"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkNetworkTopology(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<File> sourceFiles = FileManage.listFiles(FileUtils.getFile(FileManage.getNetworkTopologyDir(systemId)));
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), NETWORK_TOPOLOGY_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(sourceFiles) || CollectionUtils.isEmpty(importFiles)) {
            return Collections.emptyList();
        }
        return importFiles.stream()
                .map(ef -> checkFile(ef, sourceFiles, "网络拓扑图"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkPhotoFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<File> sourceFiles = FileManage.findPhotoFiles(systemId);
        List<SystemImportService.ImportEntityFile> importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), PHOTO_FILE_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(sourceFiles) || CollectionUtils.isEmpty(importFiles)) {
            return Collections.emptyList();
        }
        return importFiles.stream()
                .map(ef -> checkFile(ef, sourceFiles, "证据文件"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkProofFiles(@NonNull String systemId, List<SystemImportService.ImportEntityFile> importSystemFiles) {
        List<File> sourceFiles = FileManage.findProofFiles(systemId);
        var importFiles = importSystemFiles.stream()
                .filter(ef -> StringUtils.equals(ZipUtils.getModuleDirName(ef.getName()), PROOF_FILE_DIR))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(sourceFiles) || CollectionUtils.isEmpty(importFiles)) {
            return Collections.emptyList();
        }
        return importFiles.stream()
                .map(ef -> checkFile(ef, sourceFiles, "密评证明记录文件"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
    private ImportConflictDTO checkFile(SystemImportService.ImportEntityFile importFile, List<File> sourceFiles, String fieldName) {
        File sourceFile = sourceFiles.stream()
                .filter(sf -> StringUtils.equals(sf.getName(), importFile.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(sourceFile) || !sourceFile.exists()) {
            return null;
        }
        try {
            byte[] sourceData = FileUtils.readFileToByteArray(sourceFile);
            String sourceMd5 = md5Provider.digestHex16(sourceData);
            String importMd5 = md5Provider.digestHex16(importFile.getData());
            if (!StringUtils.equals(sourceMd5, importMd5)) {
                return ImportConflictDTO.builder()
                        .fieldName(fieldName)
                        .oldContent(sourceFile.getName())
                        .newContent(importFile.getName())
                        .build();
            } else {
                return null;
            }
        } catch (IOException ex) {
            log.error("验证文件是否相同失败：", ex);
            return null;
        }
    }

}
